<!--
  只针对简单需求的封装
  基础参数
  data        目标表单数据数据
  例:
  [
    {
      "name" : "name",
      "title" : "名称",
      "placeholder" : "请输入您的姓名",
      "type" : "text",
      "require" : true, // 必填项
      "readonly" : false, // 只读项
      "length" : {"min": 2,"max": 5}, // 长度校验
      // 自定义正则校验
      "rules" : [{"regex": "^abc$" ,"message": "只能输入abc", "append": ""}] append 只能填写 "g", "i", "m" 全局匹配、区分大小写的匹配和多行匹配
    }
  ]
  excludeType  忽略表单类型
  如下类型 和 以及类型说明
  表单组封装 支持以下类型的表单
  color
  date
  datetime
  sedate      开始时间和截止时间 以date类型展现
  sedatetime  开始时间和截止时间 以datetime类型展现
  month
  week
  time
  email
  file
  number
  phone 手机号
  url
  select
  entity
  注意：radio 和 checkbox 展现的方式一样 如果选项多的化请使用select
  radio
  checkbox
  image TODO 图片框

  基本表单支持 readonly 和 disabled属性
  日期相关的支持 readonly,editable 和 disabled属性

-->
<template xmlns:v-clipboard="http://www.w3.org/1999/xhtml">
  <div :id="pid" class="inputGroupCentre">
    <el-row class="row-tr" v-for="(item, row_index) in inputItemTwo" :key="row_index">
      <template v-for="(i, index) in item">
        <!-- style="background-color: #1E9FFF" 辅助调整样式 如果高度不一就会出现布局错位 -->
        <el-col v-if="i.full && index%2 != 0" class="unify-height-all" :xs="12" :sm="12" :md="12" :lg="12" :xl="12">&nbsp;</el-col>
        <el-col :xs="4" :sm="4" :md="4" :lg="3" :xl="3" :class="titleClass(i)">{{i.title}}</el-col>
        <!-- 无错误提示方式或自定义位置-->
        <el-col v-if="inputErrorExclude.indexOf(i.type) > -1"
                :xs="20"
                :sm="isFull(i, 8, 20)"
                :md="isFull(i, 8, 20)"
                :lg="isFull(i, 9, 21)"
                :xl="isFull(i, 9, 21)">
          <div v-if="i.type == 'image'">
            <div class="form-group w100">
              <el-input
                class="group-item"
                style="width: 70%" v-model="i.value"
                placeholder="请输入图片地址或从本地上传"
                :class="inputClass(i)"
                @blur="setImageUrl(i)">
                <template slot="suffix">
                  <el-tooltip class="item" effect="dark" content="点击复制" placement="bottom-end" :hide-after="2000">
                    <i class="hand" v-clipboard:copy="i.value" v-clipboard:success="clipboardSuccess">
                      <svg-icon input :icon-class="'copy'"></svg-icon>
                    </i>
                  </el-tooltip>
                </template>
              </el-input>
              <el-upload class="group-item"
                         :show-file-list="false"
                         :http-request="uploadImageHandle"
                         :before-upload="beforeImageUploadHandle"
                         :action="fileServiceUrl"
              >
                <el-button @click="setTempInput(i)">本地上传</el-button>
              </el-upload>
            </div>
            <div class="my-input-error" v-if="i.state == 'error'">
              &nbsp;<span >{{i.message}}</span>
            </div>
            <img v-if="i.value" :alt="i.title" :src="i.value" class="avatar-uploader avatar hand" @click="openBigImage">
            <img v-if="!i.value" :alt="i.title" src="static/image/default_img.png" class="avatar-uploader avatar hand" @click="openBigImage">
          </div>
        </el-col>
        <!-- 最底部错误提示方式-->
        <el-col
          v-if="inputErrorExclude.indexOf(i.type) < 0"
          :xs="20"
          :sm="isFull(i, 8, 20)"
          :md="isFull(i, 8, 20)"
          :lg="isFull(i, 9, 21)"
          :xl="isFull(i, 9, 21)">
          <el-checkbox class="w100"
                       v-if="i.type == 'radio' || i.type == 'checkbox'"
                       v-model="i.value" style="margin-top: 0.4rem">
          </el-checkbox>
          <el-select class="w100"
                     v-if="i.type == 'select'"
                     v-model="i.value"
                     :collapse-tags="!i.full"
                     :multiple="i.multiple || false"
                     :disabled= "i.disabled || false"
                     :placeholder="i.placeholder"
          >
            <el-option class="w100"
                       v-for="(item,index) in i.data"
                       :key="index"
                       :disabled="item.disabled || false"
                       :label="item.text"
                       :value="item.value">
            </el-option>
          </el-select>
          <el-select class="w100"
                     v-if="i.type == 'entity'"
                     v-model="i.value"
                     filterable
                     :filter-method="entityFilterHandler"
                     default-first-option
                     @focus="setTempInput"
                     @blur="entityOptions = entityOptionsBak"
                     :multiple="i.multiple || false"
                     :collapse-tags="!i.full"
                     :disabled= "i.disabled || false"
                     placeholder="请选择实体对象">
            <el-option
              v-for="(item, index) in entityOptions"
              :key="index"
              :label="item.text"
              :value="item.value">
              <span style="float: left">{{ item.text }}</span>
              <span style="float: right; color: #8492a6; font-size: 0.75rem">{{ item.value }}</span>
            </el-option>
          </el-select>
          <el-input
            :autofocus= "index == 0 && !i.readonly"
            :readonly= "i.readonly || false"
            :disabled= "i.disabled || false"
            v-if="baseType.indexOf(i.type) > -1"
            :type="i.type"
            :class="inputClass(i)"
            v-model="i.value"
            :maxlength = 'i.length ? i.length.max : Infinity'
            :placeholder="i.placeholder"
            @blur="!i.readonly ? handleInputChange(i,index) : null">
            <svg-icon slot="suffix" input :icon-class="i.type || ''"></svg-icon>
          </el-input>
          <el-input
            :autofocus= "index == 0 && !i.readonly"
            :readonly= "i.readonly || false"
            :disabled= "i.disabled || false"
            v-if="i.type == 'url'"
            type="text"
            :class="inputClass(i)"
            class="url-icon-prepend"
            v-model="i.value"
            :placeholder="i.title"
            @blur="!i.readonly ? handleInputChange(i,index) : null"
          >
            <template slot="prepend">
              <el-tooltip class="item" effect="dark" content="在新窗口打开" placement="bottom-start">
                <a @click="openUrl(i.value)">
                  <svg-icon input :icon-class="'href'" style="height: 36px;" ></svg-icon>
                </a>
              </el-tooltip>
            </template>
            <template slot="suffix">
              <el-tooltip class="item" effect="dark" content="点击复制" placement="bottom-end" :hide-after="2000">
                <i class="hand" v-clipboard:copy="i.value" v-clipboard:success="clipboardSuccess">
                  <svg-icon input :icon-class="'copy'"></svg-icon>
                </i>
              </el-tooltip>
            </template>
          </el-input>
          <el-input-number
            v-if="i.type == 'number'"
            v-model="i.value"
            :autofocus= "index == 0 && !i.readonly"
            :readonly= "i.readonly || false"
            :disabled= "i.disabled || false"
            controls-position="right"
            :class="inputClass(i)"
            :precision="i.precision || 0" :step="i.precision || 1"
            :max="i.range.max || Infinity"
            @change="!i.readonly ? handleInputChange(i,index) : null"
          >
          </el-input-number>
          <el-input
            :autofocus= "index == 0 && !i.readonly"
            :readonly= "i.readonly || false"
            :disabled= "i.disabled || false"
            v-if="i.type == 'time'"
            type="time"
            :class="inputClass(i)"
            v-model="i.value"
            :placeholder="i.placeholder"
            @blur="!i.readonly ? handleInputChange(i) : null"
          >
          </el-input>
          <el-date-picker
            :autofocus= "index == 0 && !i.readonly"
            :readonly= "i.readonly || false"
            :disabled= "i.disabled || false"
            :editable= "i.editable"
            v-if="i.type == 'date'"
            :class="inputClass(i)"
            v-model="i.value"
            type="date"
            placeholder="请选择日期"
            format="yyyy 年 MM 月 dd 日">
          </el-date-picker>
          <el-date-picker
            :autofocus= "index == 0 && !i.readonly"
            :readonly= "i.readonly || false"
            :disabled= "i.disabled || false"
            :editable= "i.editable"
            v-if="i.type == 'datetime'"
            :class="inputClass(i)"
            v-model="i.value"
            type="datetime"
            :placeholder="'请选择' + i.title">
          </el-date-picker>
          <el-date-picker
            :autofocus= "index == 0 && !i.readonly"
            :readonly= "i.readonly || false"
            :disabled= "i.disabled || false"
            :editable= "i.editable"
            v-if="i.type == 'sedate'"
            class="w100"
            v-model="i.value"
            type="daterange"
            range-separator="至"
            :start-placeholder="'请选择' + i.title"
            end-placeholder="结束日期">
          </el-date-picker>
          <el-date-picker
            :autofocus= "index == 0 && !i.readonly"
            :readonly= "i.readonly || false"
            :disabled= "i.disabled || false"
            :editable= "i.editable"
            v-if="i.type == 'sedatetime'"
            class="w100"
            v-model="i.value"
            type="datetimerange"
            range-separator="至"
            :start-placeholder="'请选择' + i.title"
            end-placeholder="结束时间">
          </el-date-picker>
          <div class="my-input-error">
            &nbsp;<span v-if="i.state == 'error'">{{i.message}}</span>
          </div>
        </el-col>
      </template>
      <slot></slot>
    </el-row>
  </div>
</template>

<script>
  import { get } from '@/utils/ajax.js'
  import clipboard from '@/directive/clipboard/index.js?ran=1896'
  import layer from 'layer'

  export default {
    name: 'InputGroup',
    directives: {
      clipboard
    },
    componentName: 'InputGroup',
    props: {
      data: {
        type: [Array],
        default() {
          return []
        }
      },
      excludeType: {
        type: [Array]
      }
    },
    data() {
      return {
        pid: 'ig_' + new Date().getTime(),
        fileServiceUrl: process.env.FILE_SERVICE_URL,
        entityListUrl: process.env.GET_ALL_ENTITY_DATA_URL,
        uploadData: {},
        inputItem: this.data,
        inputItemTwo: [[]], // 该域为二维数组 用于视图循环 最终要把它转换为inputItem
        entityOptions: [],
        entityOptionsBak: [],
        tempInput: [],
        exclude: this.excludeType,
        inputErrorExclude: ['image'],
        defaultRegex: {
          'email': /^\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}$/,
          'phone': /^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$/
        },
        baseType: ['email', 'phone', 'text', 'file', 'color', 'month', 'week', 'time']
      }
    },
    methods: {
      setImageUrl(input) {
        if (input['require']) {
          this.emptyVerify(input, input.value)
        }
        if (!input.value) return
        this.$set(input, 'url', input.value)
      },
      openUrl(url) {
        if (url) window.open(url)
      },
      clipboardSuccess() {
        layer.msg('已复制到剪切板')
      },
      isFull(input, defaultSize, maxSize) {
        if (input.full) {
          return maxSize
        }
        if (input.type === 'sedate' || input.type === 'sedatetime') {
          return maxSize
        }
        return defaultSize
      },
      isRequire(input) { // 是否必填
        var res = false
        if (input['require']) { // 标记必填
          res = true
        }
        return res
      },
      titleClass(input) { // 设置标题样式
        var title = 'unify-height title'
        if (this.isRequire(input)) {
          title = 'unify-height title title-required'
        }
        return title
      },
      inputClass(input) { // 设置表单样式
        return (input.state ? 'w100 verify-' + input.state : 'w100')
      },
      getEmptyMessage(inputs) {
        if (inputs.type === 'radio' || inputs.type === 'checkbox') {
          return '请您勾选' + inputs.title
        }
        if (inputs.type === 'select' || inputs.type === 'entity') {
          return inputs.multiple ? '请您至少选择一个' + inputs.title : '请您选择' + inputs.title
        }
        return inputs.title + '字段不能为空'
      },
      setVerifyMessage(input, message, succeed) { // 设置验证后提示消息 ture验证成功 false 失败 不传为初始化
        if (succeed === undefined || succeed === null) {
          this.$set(input, 'message', '')
          this.$set(input, 'state', '')
          return
        }
        if (succeed) {
          this.$set(input, 'message', '')
          this.$set(input, 'state', 'succeed')
          return
        }
        this.$set(input, 'message', message)
        this.$set(input, 'state', 'error')
      },
      emptyVerify(input, value) { // 空验证
        value = value === undefined ? '' : value
        var res = value instanceof Array ? value.length > 0 : value + '' // 如果是数组判断长度 否则强转为字符串
        this.setVerifyMessage(input, this.getEmptyMessage(input), res)
        return res
      },
      lengthVerify(input, len, value) { // 长度验证
        const minL = len.min || 0
        if (minL < 0) return true
        this.setVerifyMessage(input, input.title + '最少要填写' + minL + '个字符', minL <= value.length)
        return minL <= value.length
      },
      numberVerify(input, range, value) { // 数字范围验证 返回false即不通过
        if (!this.isRequire(input) && !value) { // 是否非必填 非必填暂不验证
          this.setVerifyMessage(input, null)
          return true
        }
        const minR = range.min || 0
        const maxR = range.max || Infinity
        this.setVerifyMessage(input, '请填写' + minR + '-' + maxR + '间的' + input.title, value >= minR && value <= maxR)
        return value >= minR && value <= maxR
      },
      emailVerify(input, value) { // 邮箱验证 返回false即不通过
        if (!this.isRequire(input) && !value) { // 是否非必填 非必填暂不验证
          this.setVerifyMessage(input, null)
          return true
        }
        var pattern = this.defaultRegex.email
        var b = pattern.test(value)
        this.setVerifyMessage(input, '请填写正确的电子邮箱账号', b)
        return b
      },
      phoneVerify(input, value) { // 手机号码验证  返回false即不通过
        if (!this.isRequire(input) && !value) { // 是否非必填 非必填暂不验证
          this.setVerifyMessage(input, null)
          return true
        }
        var pattern = this.defaultRegex.phone
        var b = pattern.test(value)
        this.setVerifyMessage(input, '请填写正确的手机号码', b)
        //  如果手机号正则验证未升级导致某手机号验证失败 使用如下判断
        // var temp = isNaN(value)
        // if (temp || value.length !== 11)
        return b
      },
      diyVerify(input, rule, value) { // 自定义正则表达式验证  返回false即不通过
        if (!this.isRequire(input) && !value) { // 是否非必填 非必填暂不验证
          this.setVerifyMessage(input, null)
          return true
        }
        var b = new RegExp(rule.regex, rule.append || '').test(value)
        this.setVerifyMessage(input, rule.message, b)
        return b
      },
      handleInputChange(input) { // 表单焦点失去时的验证输入
        var value = input.value
        if (input['require'] && !this.emptyVerify(input, value)) {
          return false
        }
        if (input['length'] && !this.lengthVerify(input, input['length'], value || '')) {
          return false
        }
        if (input['range'] && input['type'] === 'number' && !this.numberVerify(input, input['range'], value || '')) {
          return false
        }
        if (input['type'] && input['type'] === 'email' && !this.emailVerify(input, value || '')) {
          return false
        }
        if (input['type'] && input['type'] === 'phone' && !this.phoneVerify(input, value || '')) {
          return false
        }
        const rules = input.rules || []
        if (!rules) return true
        var diyRes = true
        rules.forEach((rule) => { // 自定义规则数组验证
          if (!this.diyVerify(input, rule, value || '')) {
            diyRes = false
          }
        })
        return diyRes
      },
      allInputInitVerify() { // 初始化所有表单验证
        var $this = this
        $this.lowerDimensions()
        $this.inputItem.forEach(input => {
          this.setVerifyMessage(input, null)
        })
      },
      allInputVerify() { // 验证所有表单
        var res = true
        var $this = this
        $this.lowerDimensions()
        $this.inputItem.forEach((input) => {
          if (!$this.handleInputChange(input)) {
            res = false
          }
        })
        return res
      },
      uploadImageHandle(param) { // 上传图片事件处理
        var url = this.fileServiceUrl
        console.log('====开始请求上传====' + url)
        console.log(param)
        var $this = this
        setTimeout(() => {
          console.log('======图片上传成功======')
          var testImgUrl = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1531722668316&di=92797ee9dc74dac811645db6073ef64c&imgtype=0&src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F0117e2571b8b246ac72538120dd8a4.jpg%401280w_1l_2o_100sh.jpg'
          $this.imageSuccessHandle(testImgUrl)
        }, 2000)
      },
      openBigImage() { // 点击图片后事件处理
        layer.photos({
          'photos': '#' + this.pid,
          'anim': 5
        })
      },
      imageSuccessHandle(url) { // 图片上传成功后事件处理 回显预览图
        var input = this.tempInput
        this.$set(input, 'value', url)
        this.setVerifyMessage(input, null, true)
        layer.closeAll()
      },
      assembleParam(file) {
        var data = this.uploadData
        this.$set(data, 'name', file.name)
        this.$set(data, 'size', file.size)
      },
      beforeImageUploadHandle(file) { // 图片上传前事件处理 验证图片合法性
        var input = this.tempInput
        var sizeMap = { 'KB': 1024, 'MB': 1024 * 1024 }
        var size
        var maxSize = 2 // 默认支持大小
        var unit = 'MB' // 默认支持单位
        if (file.size && input.size && Object.keys(input.size).length > 0) {
          unit = input.size.unit || unit
          unit = unit.toLocaleUpperCase()
          size = file.size / sizeMap[unit]
          maxSize = input.size.max || 2
        } else {
          size = file.size / sizeMap[unit]
        }
        if (size > maxSize) {
          var message = '上传的' + input.title + '图片大小不能超过 ' + maxSize + unit + '!'
          this.setVerifyMessage(input, message, false)
          return
        }
        if (input.support && input.support.indexOf(file.type.replace('image/', '')) < 0) {
          this.setVerifyMessage(input, '暂不支持该图片类型', false)
          return
        }
        this.assembleParam(file)
        layer.msg('上传中..', {
          'icon': 16,
          'shade': 0.3
        })
      },
      setTempInput(input) { // 设置临时变量input
        this.tempInput = input
      },
      entityFilterHandler(val) { // 实体类型搜索过滤事件处理
        var input = this.tempInput
        // 解决使用自定义过滤方法后出现的输入值消失问题  原因未知非版本问题
        try {
          this.$set(input, 'value', val)
        } catch (e) {
          console.log(e.message())
        }
        if (val) {
          this.entityOptions = this.entityOptionsBak.filter((item) => {
            return (item.text.toLowerCase().indexOf(val.toLowerCase())) === 0 || (item.value.toLowerCase().indexOf(val.toLowerCase())) === 0
          })
        } else {
          this.entityOptions = this.entityOptionsBak
        }
      },
      initInputsData() { // 初始化表单组数据 初始实体以及远程数据的下拉框
        var $this = this
        // 初始化各种需要远程加载的数据 并统一放入原json的data key中
        $this.inputItem.forEach(input => {
          var tempData = input.data || []
          // 加载下拉框
          if (input.type === 'select' && input.url && tempData.length < 1) {
            console.log('=======load select========' + 'url: ' + input.url)
            get({ url: input.url }, (res) => {
              $this.$set(input, 'data', res)
            })
          }
          // 如果含有实体对象则加载所有实体
          if (input.type === 'entity' && this.entityOptions.length < 1) {
            console.log('=======load entity select========')
            const url = this.entityListUrl
            console.log('==========url====' + url)
            get({ url: url }, (res) => {
              this.entityOptions = res
              this.entityOptionsBak = [].concat(this.entityOptions)
            })
          }
        })
      },
      higherDimensions() { // 把表单组升维处理 用于页面展示
        const $this = this
        const maxItem = 2
        const inputsLen = $this.inputItem.length
        var result = []
        if (inputsLen > maxItem) {
          for (var i = 0, len = inputsLen; i < len; i += maxItem) {
            result.push($this.inputItem.slice(i, i + maxItem))
          }
          $this.inputItemTwo = result
        } else {
          result.push($this.inputItem)
          $this.inputItemTwo = result
        }
        console.log($this.inputItemTwo)
      },
      lowerDimensions() {
        this.inputItem = [].concat.apply([], this.inputItemTwo)
      }
    },
    created() {
      this.initInputsData()
      this.higherDimensions()
    },
    watch: {
      data: {
        immediate: true, // 需要立即设置数据
        handler(data_) {
          this.inputItem = data_
        }
      },
      excludeType(types) {
        if (types.length > 0) {
          this.inputItem = this.inputItem.filter(i => {
            return types.indexOf(i.type)
          })
        }
      }
    }
  }
</script>

<style scoped>
  .inputGroupCentre >>> input{
    text-align: left;
  }

  .url-icon-prepend >>> .el-input-group__prepend {
    padding: 0 5px;
  }

  .inputGroupCentre {
    margin:0 auto;
  }
  .el-select-dropdown.is-multiple .el-select-dropdown__item.selected::after{
    content: none !important;
  }

  .unify-height{
    height: 2.5rem
  }

  .unify-height-all{
    height: 4rem;
  }

  .title{
    /*  默认input 为40px */
    padding-top: 1rem;
    text-align: center;
  }
  .title-required:before{
    content:"*";
    color: #f56c6c;
    padding-right: 0.25rem;
  }

  .row-tr{
    margin: 0.3rem 0;
  }

  .w100{
    width: 100%;
  }
  .my-input-error {
    height: 1.5rem;
    color: #f56c6c;
    font-size: 0.8rem;
    line-height: 1.2;
    padding-top: 0.25rem;
  }
  .verify-succeed >>> input {
    border-color: #67c23a ;
  }
  .verify-error >>> input{
    border-color: #f56c6c;
  }

  .avatar-uploader {
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    position: relative;
    overflow: hidden;
  }
  .avatar-uploader:hover {
    border-color: #409EFF;
  }
  .avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 178px;
    height: 178px;
    line-height: 178px;
    text-align: center;
  }
  .avatar {
    width: 178px;
    height: 178px;
    display: block;
  }
</style>
